package fightplan.views;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.Arrays;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

import fightplan.actions.UndoRedoManager;
import fightplan.actions.UndoRedoManagerProvider;
import fightplan.actions.UndoableAction;
import fightplan.bindx.GObservableList;
import fightplan.business.AbstractFighter;
import fightplan.business.Category;
import fightplan.business.Tournament;
import fightplan.core.FPExceptionHandler;
import fightplan.core.Style;

@SuppressWarnings("serial")
public class FighterChoosePanel extends JPanel implements UndoRedoManagerProvider {

	private FighterForCategoryTableModel tableModel;

	private JTable table;

	private TableRowSorter<FighterForCategoryTableModel> sorter;

	protected GObservableList<? extends AbstractFighter> allFighters;

	protected Category category;

	public FighterChoosePanel() {
		super(new BorderLayout());

		tableModel = new FighterForCategoryTableModel();
		table = new JTable(tableModel);
		table.getColumnModel().getColumn(2).setPreferredWidth(100);
		table.getColumnModel().getColumn(2).setMaxWidth(100);

		sorter = new TableRowSorter<FighterForCategoryTableModel>(tableModel);
		table.setRowSorter(sorter);
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

		JScrollPane scrollPane = new JScrollPane(table);
		scrollPane.getViewport().setBackground(Style.UNUSED_TABLE_SPACE);
		scrollPane.setPreferredSize(new Dimension(5000, 5000));

		add(scrollPane, BorderLayout.CENTER);
		add(createFilter(sorter), BorderLayout.NORTH);
		add(addAction(), BorderLayout.SOUTH);
	}

	public void setCategory(Category category, GObservableList<? extends AbstractFighter> allFighters) {
		if (this.category != null) {
			this.category.removeListDataListener(tableModel);
		}
		if (this.allFighters != null) {
			this.allFighters.removeListDataListener(tableModel);
		}

		this.category = category;
		this.allFighters = allFighters;
		tableModel.updateFighters();

		if (this.category != null) {
			category.addListDataListener(tableModel);
		}
		if (this.allFighters != null) {
			allFighters.addListDataListener(tableModel);
		}
	}

	private int calcSeed(AbstractFighter fighter) {
		int pos = 1;
		for (AbstractFighter seededFighter : category.getSeeds()) {
			if (seededFighter.equals(fighter))
				return pos;
			pos++;
		}
		return 0;
	}

	private MainView getMainView() {
		return (MainView) SwingUtilities.getWindowAncestor(this);
	}

	//

	private class FighterForCategoryTableModel extends AbstractTableModel implements ListDataListener {

		public FighterForCategoryTableModel() {
		}

		// ListDateListener

		public void intervalAdded(ListDataEvent e) {
			updateFighters();
		}

		public void intervalRemoved(ListDataEvent e) {
			updateFighters();
		}

		public void contentsChanged(ListDataEvent e) {
			updateFighters();
		}

		public void updateFighters() {
			fireTableDataChanged();
		}

		public int getColumnCount() {
			return 3;
		}

		@Override
		public String getColumnName(int columnIndex) {
			switch (columnIndex) {
			case 0:
				return "Name";
			case 1:
				return "Dojo";
			case 2:
				return "Gesetzte Position";
			}
			return null;
		}

		public Object getValueAt(int rowIndex, int columnIndex) {
			AbstractFighter fighter = category.getFighters().get(rowIndex);
			switch (columnIndex) {
			case 0:
				return fighter.getName();
			case 1:
				return fighter.getDojo();
			case 2:
				return calcSeed(fighter);
			}
			return null;
		}

		public int getRowCount() {
			return category != null ? category.getFighters().size() : 0;
		}

	}

	private static JPanel createFilter(final TableRowSorter<? extends TableModel> sorter) {
		FormLayout layout = new FormLayout("left:pref, 4dlu, pref:grow(1), 20dlu", "20dlu"); // add
																								// rows
																								// dynamically

		final JTextField textField = new JTextField();
		textField.getDocument().addDocumentListener(new DocumentListener() {

			public void changedUpdate(DocumentEvent e) {
				updateFilter();
			}

			public void insertUpdate(DocumentEvent e) {
				updateFilter();
			}

			public void removeUpdate(DocumentEvent e) {
				updateFilter();
			}

			private void updateFilter() {
				String text = textField.getText();
				if (text == null || text.trim().length() == 0) {
					sorter.setRowFilter(null);
				} else {
					sorter.setRowFilter(RowFilter.regexFilter(".*" + text + ".*"));
				}
			}
		});

		DefaultFormBuilder builder = new DefaultFormBuilder(layout);
		builder.append("Suche", textField);
		return builder.getPanel();
	}

	private JPanel addAction() {
		JPanel panel = new JPanel(new FlowLayout(SwingConstants.RIGHT));
		panel.add(createSeedButton());
		panel.add(createRemoveButton());
		panel.add(new JButton(new FighterInCategoryAddAction()));
		return panel;
	}

	private JButton createRemoveButton() {
		final Action removeAction = new UndoableAction("Entfernen", this, "Teilnehmer wieder anfügen") {
			public boolean doAction(ActionEvent e) {
				if (category.isPlanned()) {
					FPExceptionHandler.instance().showInformation("Kategorie bereits ausgelost.",
							"Keine Änderung möglich");
					return false;
				}

				boolean confirm = JOptionPane.showConfirmDialog(FighterChoosePanel.this,
						"Wollen Sie diese(n) Teilnehmer wirklich entfernen?", "Teilnehmer entfernen",
						JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION;
				if (!confirm)
					return false;
				int[] rows = table.getSelectedRows();
				int[] modelRows = new int[rows.length];
				int count = 0;
				for (int row : rows) {
					modelRows[count++] = table.convertRowIndexToModel(row);
				}
				Arrays.sort(modelRows);
				for (int i = modelRows.length - 1; i >= 0; i--) {
					category.getFighters().remove(modelRows[i]);
				}
				return true;
			}
		};
		removeAction.setEnabled(table.getSelectedRows().length >= 1);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				removeAction.setEnabled(table.getSelectedRows().length >= 1);
			}
		});
		return new JButton(removeAction);
	}

	private JButton createSeedButton() {
		final Action seedAction = new UndoableAction("Setzen", this, "Setzen rückgängig machen") {
			public boolean doAction(ActionEvent e) {
				if (category.isPlanned()) {
					FPExceptionHandler.instance().showInformation("Kategorie bereits ausgelost.",
							"Keine Änderung möglich");
					return false;
				}

				AbstractFighter fighter = category.getFighters().get(
						table.convertRowIndexToModel(table.getSelectedRow()));
				int valueCount = category.getSeeds().size() + 1;
				int seed = calcSeed(fighter);
				if (seed > 0)
					valueCount--;
				Object[] selectionValues = new Object[valueCount + 1];
				selectionValues[0] = "Ungesetzt";
				for (int i = 1; i <= valueCount; i++) {
					selectionValues[i] = i;
				}

				Object initialSelectionValue;
				if (seed > 0) {
					initialSelectionValue = seed;
				} else {
					initialSelectionValue = "Ungesetzt";
				}

				Object result = JOptionPane.showInputDialog(FighterChoosePanel.this, "Auf welche Position setzen?",
						"Position setzen", JOptionPane.QUESTION_MESSAGE, null, selectionValues, initialSelectionValue);

				if (result == null)
					return false;
				if (result == "Ungesetzt")
					result = 0;

				//

				category.getSeeds().remove(fighter);
				int newPos = (Integer) result;
				if (newPos > 0) {
					newPos = newPos - 1; // List count starts at 0
					if (newPos > category.getSeeds().size()) {
						newPos = category.getSeeds().size();
					}
					category.getSeeds().add(newPos, fighter);
				}

				table.repaint();
				return true;
			}
		};

		seedAction.setEnabled(table.getSelectedRows().length == 1);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				seedAction.setEnabled(table.getSelectedRows().length == 1);
			}
		});
		return new JButton(seedAction);
	}

	private class FighterInCategoryAddAction extends AbstractAction {

		public FighterInCategoryAddAction() {
			putValue(Action.NAME, "Teilnehmer hinzufügen");
		}

		public void actionPerformed(ActionEvent e) {
			if (category.isPlanned()) {
				FPExceptionHandler.instance().showInformation("Kategorie bereits ausgelost.", "Keine Änderung möglich");
			}

			JFrame owner = (JFrame) SwingUtilities.getWindowAncestor(FighterChoosePanel.this);
			FighterInCategoryAddDialog dialog = new FighterInCategoryAddDialog(owner);
			dialog.pack();
			dialog.setResizable(false);
			dialog.setLocationRelativeTo(owner);
			dialog.setVisible(true);
		}
	}

	public UndoRedoManager getUndoRedoManager() {
		return ((MainView) SwingUtilities.getWindowAncestor(this)).getUndoRedoManager();
	}

	private class FighterInCategoryAddDialog extends JDialog {

		private JTable table;

		private TableRowSorter<FighterChooseTableModel> sorter;

		private FighterChooseTableModel tableModel;

		public FighterInCategoryAddDialog(JFrame owner) {
			super(owner);
			setTitle("Teilnehmer auswählen");

			tableModel = new FighterChooseTableModel();
			table = new JTable(tableModel);

			sorter = new TableRowSorter<FighterChooseTableModel>(tableModel);
			table.setRowSorter(sorter);
			// table.setDefaultRenderer(Object.class, new CFCellRenderer());
			table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

			JScrollPane scrollPane = new JScrollPane(table);
			scrollPane.getViewport().setBackground(Style.UNUSED_TABLE_SPACE);
			scrollPane.setPreferredSize(new Dimension(800, 600));

			getContentPane().getInsets().left = 5;
			getContentPane().getInsets().right = 5;
			getContentPane().getInsets().top = 5;
			getContentPane().getInsets().bottom = 5;

			add(scrollPane, BorderLayout.CENTER);
			add(createFilter(sorter), BorderLayout.NORTH);
			add(buildActions(), BorderLayout.SOUTH);
		}

		public Component buildActions() {
			JPanel panel = new JPanel(new FlowLayout(SwingConstants.RIGHT));
			panel.add(new JButton(new AddAction()));
			panel.add(new JButton(new CancelAction()));
			return panel;
		}

		private class AddAction extends AbstractAction {

			public AddAction() {
				putValue(Action.NAME, "Hinzufügen");
			}

			public void actionPerformed(ActionEvent e) {
				try {
					Tournament tournament = (Tournament) getMainView().getTournament().getValue();

					boolean compatible = true;
					int[] rows = table.getSelectedRows();
					for (int row : rows) {
						int modelRow = table.convertRowIndexToModel(row);
						AbstractFighter fighter = tournament.getFighters().get(modelRow);
						if (!category.compatible(fighter)) {
							compatible = false;
							break;
						}
					}

					if (!compatible) {
						int result = JOptionPane
								.showConfirmDialog(
										FighterInCategoryAddDialog.this,
										"Sie haben min. 1 Teilnehmer gewählt, der\nnicht in die Kategorie passt.\n\nSollen alle Teilnehmer hinzugefügt werden?",
										null, JOptionPane.YES_NO_OPTION);
						if (result != JOptionPane.YES_OPTION)
							return;
					}

					for (int row : rows) {
						int modelRow = table.convertRowIndexToModel(row);
						AbstractFighter fighter = tournament.getFighters().get(modelRow);
						if (!category.contains(fighter)) {
							category.addFighter(fighter);
						}
					}

					FighterInCategoryAddDialog.this.setVisible(false);
					FighterInCategoryAddDialog.this.dispose();
				} catch (Exception x) {
					FPExceptionHandler.instance().handle(x);
				}
			}

		}

		private class CancelAction extends AbstractAction {

			public CancelAction() {
				putValue(Action.NAME, "Abbrechen");
			}

			public void actionPerformed(ActionEvent e) {
				FighterInCategoryAddDialog.this.setVisible(false);
				FighterInCategoryAddDialog.this.dispose();
			}

		}

		private class FighterChooseTableModel extends AbstractTableModel {

			public FighterChooseTableModel() {
			}

			public int getColumnCount() {
				return 2;
			}

			@Override
			public String getColumnName(int columnIndex) {
				switch (columnIndex) {
				case 0:
					return "Name";
				case 1:
					return "Dojo";
				}
				return null;
			}

			public Object getValueAt(int rowIndex, int columnIndex) {
				Tournament tournament = (Tournament) getMainView().getTournament().getValue();
				AbstractFighter fighter = tournament.getFighters().get(rowIndex);
				switch (columnIndex) {
				case 0:
					return fighter.getName();
				case 1:
					return fighter.getDojo();
				}
				return null;
			}

			public int getRowCount() {
				Tournament tournament = (Tournament) getMainView().getTournament().getValue();
				return tournament.getFighters().size();
			}
		}

	}

}
